require 'fileutils'

class FidWorkerJob < Struct.new(:job_id)

  def perform
    job = Job.find(job_id)
    raise JobAlreadyCompletedException if job.completed

    username, hostname = Worker["username"], Worker["hostname"]
    worker_output_directory = File.join(Worker["worker_output_directory"], job.id.to_s)
    local_output_directory = File.join(Rails.public_path, "images", "jobs", job.id.to_s)
    fid_directory = Worker["fid_binary_directory"]
    fid_command = File.join(fid_directory, Worker["fid_binary_name"])
    fid_parameters = build_parameter_string(job, worker_output_directory)
    command = "cd #{fid_directory} && #{fid_command} #{fid_parameters}"

    Net::SSH.start(hostname, username,
                   :keys => [APP_CONFIG["ssh_private_key_path"]],
                   :passphrase => APP_CONFIG["ssh_passphrase"]) do |ssh|
      ssh.sftp.connect do |sftp|
        sftp.mkdir!(worker_output_directory) rescue nil
        sftp.file.open(File.join(worker_output_directory, "mol_file"), "w") do |f|
          f.write(job.mol_file)
        end
        sftp.file.open(File.join(worker_output_directory, "ms_file"), "w") do |f|
          f.write(job.ms_file)
        end
      end

      log "Executing job ##{job.id} with `\"#{command}\"`"
      ssh.exec!(command)

      FileUtils.mkdir_p(local_output_directory)
      ssh.sftp.download!(worker_output_directory, local_output_directory,
                         :recursive => true, :requests => 1)
    end

    Job.transaction do
      Fragment.transaction do
        File.readlines(File.join(local_output_directory, "fid.out")).each do |atomline|
          options = extract_options_from_atomline(atomline).merge(:selected => false)
          job.fragments.create!(options)
        end
      end
      job.completed = true
      job.save!
    end

    if job.reload.user.email && job.user.send_email && Time.zone.now - job.created_at > 30.seconds
      Mailer.send_later(:deliver, Mailer.create_job_notification(job))
    end
    return true

  rescue
    log $!
    raise
  end

  private

  def build_parameter_string(job, output_directory)
    [
     "-d #{job.measurement_error}",
     "-i #{job.intensity_threshold}", "-bb",
     "-rr #{job.rr}",
     "-cuts #{job.cuts}",
     "-o #{File.join(output_directory, "fid.out")}",
     "-svg -imagedir #{output_directory}/", # Note: FiD requires the trailing slash
     "#{File.join(output_directory, "mol_file")}",
     "#{File.join(output_directory, "ms_file")}"
    ].join(" ")
  end

  def log(msg)
    begin
      Rails.logger.info "  [worker] #{msg}"
    ensure
      Rails.logger.flush
    end
  end

  def extract_options_from_atomline(atomline)
    values = atomline.split("\t")
    {
      :fragment => values[0],
      :peaknum  => values[1],
      :peakmass => values[2],
      :mass     => values[3],
      :massdiff => values[4],
      :formula  => values[5],
      :atombits => values[6],
      :rank     => values[7],
      :score    => values[8],
      :bb       => values[9],
      :bbh      => values[10],
      :bbs      => values[11],
      :abb      => values[12],
      :sbb      => values[13],
      :swd      => values[14],
      :rr       => values[15] == "yes" ? true : false
    }
  end
end

class JobAlreadyCompletedException < Exception
end
